﻿/* 
 * Evo
 * 
 * Marcin Krupowicz (marcin@krupowicz.eu)
 * Barbara Szymanek (basia.szymanek@gmail.com)
 * 
 * January, 2009
 *
 */

using System;
using System.Collections.Generic;
using System.Text;
using Evo.Operators;
using System.IO;

namespace Evo
{
	/// <summary>
	/// SelfAdaptiveEvolutionaryAlgorithm is evolutionary algorithm based on the idea of island model and autoadaptive techniques.
	/// </summary>
	/// <typeparam name="T">Type of individuals on which algorithm is operating</typeparam>
	public class SelfAdaptiveEvolutionaryAlgorithm<T> : IAlgorithm where T : IIndividual
	{
		private int numberOfSubPopulations;
		private int subPopulationSize;

		private List<EvolutionaryAlgorithmWorker<T>> workers;
		private EvaluateChromosomeDelegate objectiveFunction; //objective function for internal algorithms
		private bool verbose;
        private TextWriter verboseOutput;
        private Population<T> population;
        
        //Population of probabilities (population of meta algorithm) and its operators
        private Population<RealIndividual> ppbPopulation;
        private List<IOperator<RealIndividual>> ppbOperators;
        int iterationNumber;
        int workerIteration;

		/// <summary>
		/// Number of subpopulations which will be evaluated simultanously
		/// </summary>
		public int NumberOfSubPopulations
		{
			get { return numberOfSubPopulations; }
		}

        /// <summary>
        /// Best individual
        /// </summary>
        public T Best
        {
            get
            {
                return this.population.Best;
            }
        }

		/// <summary>
		/// Output where algorithm should print information while working
		/// </summary>
        public TextWriter VerboseOutput
        {
            get { return verboseOutput; }
            set { verboseOutput = value; }
        }

        /// <summary>
        /// Best combination of probability for operators 
        /// </summary>
        public RealIndividual BestPpbCombination
        {
            get
            {
               return this.ppbPopulation.Best;
            }
        }

        /// <summary>
        /// Size of each subpopulation
        /// </summary>
        public int SubPopulationSize
		{
			get { return subPopulationSize; }
		}

		/// <summary>
		/// Whole population from all internal algorithms
		/// </summary>
		public Population<T> Population
		{
			get { return population; }
			set { population = value; }
		}

		/// <summary>
		/// Objective function
		/// </summary>
		public EvaluateChromosomeDelegate ObjectiveFunction
		{
			get { return  objectiveFunction; }
			set 
			{ 
				objectiveFunction = value;
				foreach (EvolutionaryAlgorithmWorker<T> worker in this.workers)
					worker.ObjectiveFunction = value;
			}
		}

		/// <summary>
		/// Create SelfAdaptiveEvolutionaryAlgorithm instance
		/// </summary>
		/// <param name="numberOfSubPopulations">Number of subpopulations which will be evaluated simultanously</param>
		/// <param name="subPopulationSize">size of each subpopulation</param>
		/// <param name="bigIterations">number of iterations of self adaptive evolutionary algorithm</param>
		/// <param name="smallIterations">number of iterations performed each time by internal algorithms</param>
		/// <param name="chromosomeLengt">length of chromosome</param>
		/// <param name="operators">operators which will be applicated to internal algorithms</param>
		/// <param name="verbose">verbose mode</param>
		public SelfAdaptiveEvolutionaryAlgorithm(int numberOfSubPopulations, int subPopulationSize, int bigIterations, 
			int smallIterations, int chromosomeLength, List<IOperator<T>> operators, bool verbose)
		{
			this.subPopulationSize = subPopulationSize;
            this.numberOfSubPopulations = numberOfSubPopulations;
			this.verbose = verbose;
            this.iterationNumber = bigIterations;
            this.workerIteration = smallIterations;

			this.ppbPopulation = Population<RealIndividual>.GenerateRandom(numberOfSubPopulations, operators.Count);
            this.ppbOperators = new List<IOperator<RealIndividual>>();

			this.population = Population<T>.GenerateRandom(subPopulationSize * numberOfSubPopulations, chromosomeLength);
           
            workers = new List<EvolutionaryAlgorithmWorker<T>>();
            EvolutionaryAlgorithmWorker<T> worker;

			for(int i=0; i<numberOfSubPopulations; i++)
			{
				worker = new EvolutionaryAlgorithmWorker<T>(subPopulationSize, chromosomeLength, smallIterations, false);
				worker.Operators = operators; //NOTE: it's ok only in sequential running
				workers.Add(worker);
			}
		}

		/// <summary>
		/// Add operator for SelfAdaptiveEvolutionaryAlgorithm (*not* for internal algorithms)
		/// </summary>
		/// <param name="op">Evolutionary operator</param>
		public void AddOperator(IOperator<RealIndividual> op)
		{
            this.ppbOperators.Add(op);
		}

		public void IntroduceYourself()
		{
			//TODO:
		}

		/// <summary>
		/// Run algorithm
		/// </summary>
		public void RunAlgorithm()
		{
            if (ppbOperators.Count == 0)
                throw new Exception("There is no operator for ppbVectors");

            if (iterationNumber <= 0)
                throw new Exception("Iteration numbers must be greater then 0");

            if (objectiveFunction == null)
                throw new Exception("Objective function must be set");
            
            int iterationCounter = 0;

            while (iterationCounter < iterationNumber)
            {
                dividePopulationToWorkers();
                
                setPpbVectorsToWorkers();
                
                runWorkers();
                
                getPopulationFromWorkers();
                
                doRealIndividualEvolution();

                iterationCounter++;

                if (verbose && verboseOutput != null)
                    outputStatistic(iterationCounter*workerIteration);
            }

            
            if (verbose && verboseOutput != null)
            {
                workers[0].VerboseOutput = verboseOutput;
                workers[0].Verbose = true;
                workers[0].IntroduceYourself();
            }
		}

        private void outputStatistic(int iteration)
        {
			String thisIterationStatistic = String.Format(
				System.Globalization.CultureInfo.InvariantCulture.NumberFormat,
				"{0}, {1}, {2}, {3}, {4}, {5}",
				iteration,
				Math.Abs(ppbPopulation.Best.Value),
				//Math.Abs(ppbPopulation.Worst.Value),
				//ppbPopulation.Best,
				//ppbPopulation.Worst,
				Math.Abs(population.Best.Value),
				Math.Abs(population.Worst.Value),
				Math.Abs(population.Average),
				population.StandardDeviation);
                //population.Best);

            verboseOutput.WriteLine(thisIterationStatistic);
        }

        /// <summary>
        /// Divide whole population to sub-populations for workers use
        /// it`s not random version yet, but if we do not sort population it can work
        /// </summary>
        private void dividePopulationToWorkers()
        {
            for (int i = 0; i < numberOfSubPopulations; i++)
            {
                List<T> individuals = new List<T>();
                for ( int j = i; j<this.population.Size; j+=numberOfSubPopulations)
                    individuals.Add(this.population[j]);

                this.workers[i].Population = new Population<T>(individuals);
            }
        }

        /// <summary>
        /// Start workers
        /// </summary>
        private void runWorkers()
        {
            foreach (EvolutionaryAlgorithmWorker<T> worker in this.workers)
                worker.RunAlgorithm();
        }

        /// <summary>
        /// Set probability vectors to workers
        /// </summary>
        private void setPpbVectorsToWorkers()
        {
            int i = 0;
            foreach (EvolutionaryAlgorithmWorker<T> worker in this.workers)
            {
                worker.PPBIndividual = this.ppbPopulation[i];
                i++;
            }
        }

        /// <summary>
        /// Set population from sub-populations
        /// </summary>
        private void getPopulationFromWorkers()
        {
            List<T> allIndividuals = new List<T>();

            foreach(EvolutionaryAlgorithmWorker<T> worker in this.workers)
                allIndividuals.AddRange(worker.Population.Individuals);

            this.population = new Population<T>(allIndividuals);
        }

        /// <summary>
        /// Evaluate population of probability vectors
        /// </summary>
        private void doRealIndividualEvolution()
        {
            foreach (IOperator<RealIndividual> op in this.ppbOperators)
            {
                this.ppbPopulation = op.Execute(this.ppbPopulation);
                foreach (RealIndividual ind in this.ppbPopulation.Individuals)
                    ind.Evaluate();

				this.ppbPopulation.Evaluate(ScalingType.None);
            }
        }

	}
}
